/**
 * 本文件创建于 :2009-12-30<br>
 * 
 * @version 1.0
 */

package com.monlyu.dao;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Transient;

import org.hibernate.EntityMode;
import org.hibernate.SessionFactory;
import org.hibernate.metadata.ClassMetadata;

import com.monlyu.listener.InitDataListener;
import com.monlyu.util.ClassUtil;
import com.monlyu.util.NumberUtil;
import com.monlyu.util.WebOptUtil;

/**
 * 主键生成器
 * 
 * @author monlyu
 */
public class KeyGenerator {

    private final SessionFactory factory;
    private static Map<Class<?>, Boolean> classIsLongIdFalg = new HashMap<Class<?>, Boolean>();
    private static Map<SessionFactory, KeyGenerator> keyMap = new HashMap<SessionFactory, KeyGenerator>();
    private static Map<Class<?>, Long> idMap = new HashMap<Class<?>, Long>();
    private Long cluster = WebOptUtil.APP_ARGS.getClusterNum();

    private KeyGenerator(SessionFactory factory) {
        this.factory = factory;
    }

    /**
     * 获取主键生成类的实例
     * 
     * @since 2010-1-1
     */
    public static KeyGenerator createKeyGenerator(SessionFactory factory){
        KeyGenerator keyGenrator = keyMap.get(factory);
        if (keyGenrator == null) {
            keyGenrator = new KeyGenerator(factory);
            keyMap.put(factory, keyGenrator);
        }
        return keyGenrator;
    }

    /**
     * 获取有个实体类的的主键标示
     * 
     * @author monlyu
     * @since 2010-1-1
     */
    private Class<?> getIdType(Object entity){
        return getMetadata(entity).getIdentifierType().getReturnedClass();
    }

    /**
     * 设置对象的ID值
     * 
     * @since 2010-1-1
     */
    public void setId(Object entity, Serializable id){
        getMetadata(entity).setIdentifier(entity, id, EntityMode.POJO);
    }

    /**
     * 获取某个类的Classmata
     * 
     * @author monlyu
     * @since 2010-1-1
     */
    private ClassMetadata getMetadata(Object entity){
        return factory.getClassMetadata(ClassUtil.getClass(entity));
    }

    /**
     * 是否为实体创建新的ID
     * 
     * @since 2010-1-1
     */
    public boolean checkId(Object entity){
        if (!isLongId(entity)) {
            return false;
        }
        final Long oriId = getEntityId(entity);
        Long entityId = oriId;
        if (oriId < 0) {
            Long distributedId = getDistrId(entity, oriId);
            entityId = distributedId;
        }
        if (getIdType(entity) == Integer.class) {
            setId(entity, entityId.intValue());
        } else {
            setId(entity, entityId);
        }
        return true;
    }

    /**
     * 获取分配的ID值
     * 
     * @author monlyu
     * @since 2010-1-2
     */
    private Long getDistrId(final Object entity, Long oriId){
        final Class<?> clazz = ClassUtil.getClass(entity);
        synchronized (clazz) {
            Long savedId = idMap.get(clazz);
            if (savedId == null) {
                savedId = getMaxId(clazz);
            }
            if (oriId < 0) {
                savedId = savedId + cluster;
                idMap.put(clazz, savedId);
                return savedId;
            }
            // 有主键
            if (oriId > savedId) {
                idMap.put(clazz, oriId);
            }
            return oriId;
        }
    }

    /**
     * 获取ID类
     * 
     * @author monlyu
     * @since 2010-1-2
     */
    @Transient
    private Long getMaxId(Class<?> clazz){
        final String idClumer = factory.getClassMetadata(clazz).getIdentifierPropertyName();
        StringBuilder builder = new StringBuilder();
        builder.append("select max(t.").append(idClumer);
        builder.append(") from ").append(clazz.getName());
        builder.append(" t");
        Number maxId = (Number) InitDataListener.getDao().findOne(builder.toString());
        long id = maxId == null ? NumberUtil.LONG_ZERO : maxId.longValue();
        return id;
    }

    /**
     * 获取实体类的id值
     */
    private long getEntityId(Object entity){
        Serializable idVal = getMetadata(entity).getIdentifier(entity, EntityMode.POJO);
        if (idVal == null) {
            return -1;
        }
        return ((Number) idVal).longValue();
    }

    /**
     * 判断实体类的Id是否为长整型
     * 
     * @author monlyu
     * @since 2010-1-2
     */
    private boolean isLongId(Object entity){
        Class<?> clazz = ClassUtil.getClass(entity);
        if (classIsLongIdFalg.get(clazz) != null) {
            return classIsLongIdFalg.get(clazz);
        }
        final Class<?> idType = getIdType(entity);
        final boolean isLong = (idType == Long.class || idType == Integer.class);
        classIsLongIdFalg.put(clazz, isLong);
        return isLong;
    }

    /**
     * 判断是不是字符串key
     */
    public boolean isStringkey(Object entity){
        return getIdType(entity) == String.class;
    }
}
